#include "cfraction.h"
#include "cintmath.h"

/**
 * CFraction - creates fraction 0/1
 */
CFraction::CFraction() {
  set(0, 1);
}
//---------------------------------------------------------------------------

/**
 * CFraction - creates fraction num/1
 * @param num integer numerator
 */
CFraction::CFraction(int num) {
  set((long) num, 1);
}
//---------------------------------------------------------------------------

/**
 * CFraction - creates fraction num/1
 * @param num long integer numerator
 */
CFraction::CFraction(long num) {
  set(num, 1);
}
//---------------------------------------------------------------------------

/**
 * CFraction - creates fraction from numerator and denominator
 * @param numerator
 * @param denominator
 */
CFraction::CFraction(long numerator, long denominator) {
  set(numerator, denominator);
}
//---------------------------------------------------------------------------

/**
 * operator+= - adds given fraction to this fraction
 * @param f
 * @return this
 */
CFraction& CFraction::operator += (const CFraction &f) {
  long d(f.getDenominator()), n(f.getNumerator());
  toCommonBase(n, d);
  numerator += n;
  reduce();
  return *this;
}
//---------------------------------------------------------------------------

/**
 * operator-= - subtracts given fraction from this fraction
 * @param f
 * @return this
 */
CFraction& CFraction::operator -= (const CFraction &f) {
  long d(f.getDenominator()), n(f.getNumerator());
  toCommonBase(n, d);
  numerator -= n;
  reduce();
  return *this;
}
//---------------------------------------------------------------------------

/**
 * operator*= - multiplies this fraction by given fraction
 * @param f
 * @return this
 */
CFraction& CFraction::operator *= (const CFraction &f) {
  numerator *= f.getNumerator();
  denominator *= f.getDenominator();
  reduce();
  return *this;
}
//---------------------------------------------------------------------------

/**
 * operator/= - divides this fraction by given fraction
 * @param f
 * @return this
 */
CFraction& CFraction::operator /= (const CFraction &f) {
  numerator *= f.getDenominator();
  denominator *= f.getNumerator();
  reduce();
  return *this;
}
//---------------------------------------------------------------------------

/**
 * operator+ - adds two fractions
 * @param f
 * @return soucet
 */
CFraction CFraction::operator + (const CFraction &f) const {
  return CFraction(*this) += f;
}
//---------------------------------------------------------------------------

/**
 * operator- - subtracts two fractions
 * @param f
 * @return rozdil
 */
CFraction CFraction::operator - (const CFraction &f) const {
  return CFraction(*this) -= f;
}
//---------------------------------------------------------------------------

/**
 * operator* - multiplies two fractions
 * @param f
 * @return soucin
 */
CFraction CFraction::operator * (const CFraction &f) const {
  return CFraction(*this) *= f;
}
//---------------------------------------------------------------------------

/**
 * operator/ - divides two fractions
 * @param f
 * @return podil
 */
CFraction CFraction::operator / (const CFraction &f) const {
  return CFraction(*this) /= f;
}
//---------------------------------------------------------------------------

/**
 * operator== - compares two fractions
 * @param f
 * @return true if given fractions are equal
 */
bool CFraction::operator == (const CFraction &f) const {

  // Not necessary to reduce (reduction is performed after every calculation)
  return (numerator == f.getNumerator() && denominator == f.getDenominator());
}
//---------------------------------------------------------------------------

/**
 * operator!= - compares two fractions
 * @param f
 * @return true if given fractions are not equal
 */
bool CFraction::operator != (const CFraction &f) const {
  return !(*this == f);
}
//---------------------------------------------------------------------------

/**
 * operator> - compares two fractions
 * @param f
 * @return true if first fraction is greater than second fraction
 */
bool CFraction::operator > (const CFraction &f) const {
  CFraction tmp(*this);
  long d = f.getDenominator(), n = f.getNumerator();
  return tmp.toCommonBase(n, d).getNumerator() > n;
}
//---------------------------------------------------------------------------

/**
 * operator< - compares two fractions
 * @param f
 * @return true if first fraction is smaller than second fraction
 */
bool CFraction::operator < (const CFraction &f) const {
  CFraction tmp(*this);
  long d = f.getDenominator(), n = f.getNumerator();
  return tmp.toCommonBase(n, d).getNumerator() < n;
}
//---------------------------------------------------------------------------

/**
 * operator>= - compares two fractions
 * @param f
 * @return true if first fraction is greater/equal to second fraction
 */
bool CFraction::operator >= (const CFraction &f) const {
  return !(*this < f);
}
//---------------------------------------------------------------------------

/**
 * operator<= - compares two fractions
 * @param f
 * @return true if first fraction is smaller/equal to second fraction
 */
bool CFraction::operator <= (const CFraction &f) const {
  return !(*this > f);
}
//---------------------------------------------------------------------------

/**
 * operator! - checks whether fraction is equal to zero
 * @return true if fraction is zero (numerator is zero)
 */
bool CFraction::operator ! () const {
  return numerator == 0;
}
//---------------------------------------------------------------------------

/**
 * operator- - negates fraction
 * @return negation of fraction
 */
CFraction CFraction::operator - () const {
  return *this * CFraction(-1);
}
//---------------------------------------------------------------------------

/**
 * set - sets numerator and denominator
 * @param numerator
 * @param denominator
 */
void CFraction::set(long numerator, long denominator) {
  this->numerator = numerator;
  this->denominator = denominator;
  reduce();
}
//---------------------------------------------------------------------------

/**
 * getNumerator - returns numerator
 * @return numerator
 */
long CFraction::getNumerator() const {
  return numerator;
}
//---------------------------------------------------------------------------

/**
 * getDenominator - returns denominator
 * @return denominator
 */
long CFraction::getDenominator() const {
  return denominator;
}
//---------------------------------------------------------------------------

/**
 * reduce - reduce fraction
 */
void CFraction::reduce() {

  // Reduce numerator and denominator
  long tmp = CIntMath<long>::gcd(numerator, denominator);
  denominator /= tmp;
  numerator /= tmp;

  // Reduce signs (sign of fraction is holded by numerator)
  if (denominator < 0) {
    denominator *= -1;
    numerator *= -1;
  }
}
//---------------------------------------------------------------------------

/**
 * toCommonBase - transforms fraction to common base with given num. and den.
 * @param numerator
 * @param denominator
 * @return this
 */
CFraction& CFraction::toCommonBase(long &numerator, long &denominator) {

  // Lowest common multiplicant of denominators
  long tmpD = CIntMath<long>::lcm(this->denominator, denominator);

  // Calculate common denominator and new numerators
  numerator *= tmpD / denominator;
  this->numerator *= tmpD / this->denominator;
  this->denominator = tmpD;
  denominator = tmpD;

  return *this;
}
//---------------------------------------------------------------------------
